package com.cango.student.http;

import com.alibaba.fastjson.JSON;
import com.cango.student.SerializationUtil;
import com.cango.student.WsResult;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpRequest;
import org.apache.http.NoHttpResponseException;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.protocol.HttpContext;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.client.RestTemplate;

import javax.net.ssl.SSLException;
import javax.net.ssl.SSLHandshakeException;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.net.UnknownHostException;
import java.util.List;
import java.util.concurrent.TimeUnit;

public class RestTemplateSample {

    // 请求重试处理
    private static HttpRequestRetryHandler httpRequestRetryHandler = new HttpRequestRetryHandler() {
        public boolean retryRequest(IOException exception,
                                    int executionCount, HttpContext context) {

            HttpClientContext clientContext = HttpClientContext
                    .adapt(context);
            HttpRequest request = clientContext.getRequest();
            // 如果请求是幂等的，就再次尝试
            if (!(request instanceof HttpEntityEnclosingRequest)) {
                return true;
            }

            // 可以重试2次
            if (executionCount < 2) {
                return true;
            }
            if (exception instanceof NoHttpResponseException) {// 如果服务器丢掉了连接，那么就重试
                return true;
            }
            if (exception instanceof SSLHandshakeException) {// 不要重试SSL握手异常
                return false;
            }
            if (exception instanceof InterruptedIOException) {// 长时间内都没有建立socket连接超时，主要发生在请求了一个不存在的服务器。
                return false;
            }
            if (exception instanceof UnknownHostException) {// 目标服务器不可达
                return false;
            }
            if (exception instanceof ConnectTimeoutException) {// 连接被拒绝
                return false;
            }
            if (exception instanceof SSLException) {// SSL握手异常
                return false;
            }

            return false;
        }
    };

    public static void main(String[] args) throws Exception {
        PoolingHttpClientConnectionManager clientConnectionManager = new PoolingHttpClientConnectionManager();
        clientConnectionManager.setMaxTotal(1000);
        clientConnectionManager.setDefaultMaxPerRoute(100);

        // 是建立一个监控线程，来专门回收由于长时间不活动而被判定为失效的连接。
        // 这个监控线程可以周期性的调用ClientConnectionManager类的closeExpiredConnections()方法来关闭过期的连接，回收连接池中被关闭的连接。
        // 它也可以选择性的调用ClientConnectionManager类的closeIdleConnections()方法来关闭一段时间内不活动的连接。
        clientConnectionManager.closeExpiredConnections();
        clientConnectionManager.closeIdleConnections(30,TimeUnit.SECONDS);


        RequestConfig requestConfig = RequestConfig.custom()
                .setSocketTimeout(7200)
                .setConnectTimeout(2000)
                .setConnectionRequestTimeout(2000)
                .build();

        HttpClientBuilder httpClientBuilder = HttpClientBuilder.create()
                .setSSLContext(AsyHttpClient.createIgnoreVerifySSL())
                .setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE)
                .setConnectionManager(clientConnectionManager)
                .setRetryHandler(httpRequestRetryHandler)
                .setDefaultRequestConfig(requestConfig);

        HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(httpClientBuilder.build());
        requestFactory.setReadTimeout(5000);


        RestTemplate restTemplate = new RestTemplate(requestFactory);

        List<HttpMessageConverter<?>> messageConverters = restTemplate.getMessageConverters();
        messageConverters.add(new MyConverter());

        String url = "http://10.43.23.211:8080/zl/binaryBodyResponse";
        WsResult from = new WsResult();
        HttpHeaders headers = new HttpHeaders();
        headers.add("Content-Type", "binary/octet-stream");
        HttpEntity<byte[]> formEntity = new HttpEntity<>(SerializationUtil.serialize(from), headers);


        long start = System.currentTimeMillis();
        for (int i = 50; i > 0; i--) {

            ResponseEntity<WsResult> entity = restTemplate.postForEntity(url, formEntity, WsResult.class, new Object());

            System.out.println(JSON.toJSONString(entity.getBody()));

        }
        System.out.println("cost is:" + (System.currentTimeMillis() - start));

    }

}
